/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tsb.xmlparser;

import csm.uttils.generics.Roster;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.tree.DefaultMutableTreeNode;
import tsb.xmlparser.exceptions.OnParsingException;

/**
 *
 * @author Carlos S. Moreno <CarlosS.Moreno@gmail.com>
 */
public class XmlParser {

    private Stack<String> stack;
    private Stack<String[]> params;
    private Pattern Patt;
    private String xml;

    /**
     * XmlParser constructor. Recives the XML code to be parsed.
     * @param xml string code to be parsed.
     */
    public XmlParser(String xml) {
        Patt = Pattern.compile("<[^>]+>");
        this.xml = xml;
        stack = new Stack<String>();
        params = new Stack<String[]>();
    }

    /**
     * Parse the given XML String on the constructor to a tree.
     * @return A Tree structure from the original XML
     * @throws OnParsingException
     */
    public Node parseFromXML() throws OnParsingException {
        Roster<Node> nodes = new Roster<Node>();
        Node root = new Node();

        if (xml.length() < 1) {
            return root;//Si el texto esta vacio.
        }

        try {
            while (xml.length() > 0) {
                if (!xml.startsWith("<")) {
                    int x = xml.indexOf("<");
                    String aux;
                    if (x != -1) {
                        aux = xml.substring(0, x);
                    } else {
                        aux = "";
                    }
                    String[] props = params.pop();
                    String tag = stack.pop();
                    Node newNode = new Node(tag, aux);

                    if (props.length > 1) {
                        String[] attribute;
                        for (int i = 1; i < props.length; i++) {
                            attribute = props[i].split("=");
                            if (attribute.length > 1) {
                                newNode.addPropertie(attribute[0], attribute[1]);
                            }
                        }
                    }//End If
                    nodes.addLast(newNode);
                    x = xml.indexOf(tag);
                    xml = xml.substring(x + tag.length() + 1);
                    continue;
                }//End If

                Matcher matcher = Patt.matcher(xml);//Prepara el patron de Expresion Regular
                if (matcher.find()) {//Busca una coincidencia
                    String subseq = matcher.group(0);
                    String auxNode = subseq.substring(1, subseq.length() - 1);
                    int x = xml.indexOf(auxNode);//Obtiene el contenido entre los signos "<" y ">"
                    xml = xml.substring(x + auxNode.length() + 1);//Quita el tag de la cadena de XML
                    if (auxNode.endsWith("/")) {//Si es un tag autocontenido
                        Node nTree = new Node(auxNode, "");
                        nodes.addLast(nTree);
                    } else if (auxNode.startsWith("/")) {//Si es el cierre the un tag
                        if (stack.size() > 0
                                && auxNode.substring(1,
                                auxNode.length()).equalsIgnoreCase(stack.lastElement())) {
                            String name = stack.pop();
                            root.setName(name);
                            break;
                        }
                    } else if (xml.startsWith("<")) {//Si es la inicio de un Tag
                        String info[] = auxNode.split(" ");
                        auxNode = info[0].trim();
                        if (info.length > 1) {
                            params.push(info);
                        } else {
                            params.push(new String[]{});
                        }
                        stack.add(auxNode);
                        nodes.addLast(parseFromXML());
                    } else {
                        String data[] = auxNode.split(" ");
                        auxNode = data[0].trim();
                        if (data.length > 1) {
                            params.push(data);
                        } else {
                            params.push(new String[]{});
                        }
                        stack.add(auxNode);
                    }//End If
                }//End If
            }//End While
        }//End Try
        catch (Exception e) {
            throw new OnParsingException("[E]Parsing procces fail!");
        }//End Catch

        root.setNodes(nodes);//Agrega los nodos hijos al nodo que corresponde
        if (!params.empty()) {//Y si hay parametros para el tag, los agrega al Node
            String[] props = params.pop();
            if (props.length > 1) {
                for (int i = 1; props.length > i; i++) {
                    String[] attribute = props[i].split("=");
                    if (attribute.length > 1) {
                        int lastIndex = 1;
                        if (!attribute[1].endsWith("\""))
                            lastIndex = 0;
                        root.addPropertie(attribute[0], attribute[1].substring(1,
                                attribute[1].length() - lastIndex));
                    } else {
                        String[] preAttribute = props[i - 1].split("=");
                        String propertie = root.getProperties().remove(preAttribute[0]);
                        root.getProperties().put(preAttribute[0],
                                propertie + " " + attribute[0].substring(0,
                                attribute[0].length() - 1));
                    }

                }

            }
        }
        //System.out.println(nodes.toString());
        return root;
    }

    /**
     * Parse to XML the info on the Tree given on the parameters
     * @param root The root of the tree to convert to XML
     * @return An XML String
     */
    public String parseToXML(DefaultMutableTreeNode root) {
        String xmlToSave = "";
        if (root.getChildCount() > 0) {
            for (Enumeration e = root.children(); e.hasMoreElements();) {
                DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement();
                xmlToSave += getXML(n);
            }
        }
        return xmlToSave;
    }

    private String getXML(DefaultMutableTreeNode treeNode) {
        Node node = ((Node) treeNode.getUserObject());
        String tagName = node.getName();
        String xmlToSave = "<" + tagName;
        if (node.getProperties() != null) {
            Iterator it = node.getProperties().entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry e = (Map.Entry) it.next();
                xmlToSave += " " + e.getKey() + "=\"" + e.getValue() + "\"";
            }
        }
        xmlToSave += ">";
        if (node.getValue() != null) {
            xmlToSave += node.getValue();
        }
        if (treeNode.getChildCount() > 0) {
            for (Enumeration e = treeNode.children(); e.hasMoreElements();) {
                DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement();
                xmlToSave += getXML(n);
            }
        }
        xmlToSave += "</" + tagName + ">";
        return xmlToSave;
    }

//    @Deprecated
//    public String parseToXML (Node root)
//    {
//        return "Not implemented yet.";
//    }

}
